﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace NinjaToolbox.PhysicsSystem
{
    public class PhysicsSystemManager : MonoBehaviour
    {
        static bool applicationIsQuitting = false;

        static PhysicsSystemManager mInstance;
        public static PhysicsSystemManager Instance
        {
            get
            {
                if (applicationIsQuitting) return null;

                if (mInstance == null)
                {
                    mInstance = new GameObject("[PhysicsSystemManager]").AddComponent<PhysicsSystemManager>();
                    UnityEngine.Object.DontDestroyOnLoad(mInstance.gameObject);
                }

                return mInstance;
            }
        }

        public struct TriggerInfo
        {
            public PhysicsObject PhysicsObject { get; set; }
            public ITriggerObject TriggerObject { get; set; }
            public bool RemoveMark { get; set; }

            public List<TriggerInfo> TriggerEnterList { get; set; }
        }

        bool mIsUpdateing;
        List<TriggerInfo> mTriggerInfoList;
        List<ColliderObject> mColliderObjectList;

        public List<ColliderObject> ColliderObjectList { get { return mColliderObjectList; } }


        public void RegistColliderObject(ColliderObject colliderObject)
        {
            mColliderObjectList.Add(colliderObject);
        }

        public void UnregistColliderObject(ColliderObject colliderObject)
        {
            mColliderObjectList.Remove(colliderObject);
        }

        public bool IsIntersectWithSceneColliders(OBBBox obbBox, out OBBBox firstIntersect)
        {
            firstIntersect = default(OBBBox);

            for (int i = 0, iMax = mColliderObjectList.Count; i < iMax; i++)
            {
                var item = mColliderObjectList[i];

                if (item.ColliderShape.Intersects(obbBox))
                {
                    firstIntersect = item.ColliderShape;
                    return true;
                }
            }

            return false;
        }

        public void RegistTriggerObject(ITriggerObject triggerObject, PhysicsObject physicsObject)
        {
            mTriggerInfoList.Add(new TriggerInfo() { TriggerObject = triggerObject, PhysicsObject = physicsObject, TriggerEnterList = new List<TriggerInfo>(mTriggerInfoList.Count) });
        }

        public void UnregistTriggerObject(ITriggerObject triggerObject)
        {
            var index = mTriggerInfoList.FindIndex(m => m.TriggerObject == triggerObject);

            if (mIsUpdateing)
            {
                var temp = mTriggerInfoList[index];
                temp.RemoveMark = true;
                mTriggerInfoList[index] = temp;
            }
            else
            {
                mTriggerInfoList.RemoveAt(index);
            }
        }

        public void ForeachRegistedPhysicsObjects(Action<PhysicsObject> action)
        {
            for (int i = 0, iMax = mTriggerInfoList.Count; i < iMax; i++)
            {
                if (action != null)
                    action(mTriggerInfoList[i].PhysicsObject);
            }
        }

        public bool IsHitToOtherPhysicsObjects(PhysicsObject physicsObject, Action<PhysicsObject> onHit = null)
        {
            var result = false;

            for (int i = 0, iMax = mTriggerInfoList.Count; i < iMax; i++)
            {
                var item = mTriggerInfoList[i];
                if (physicsObject == item.PhysicsObject) continue;

                var isHit = physicsObject.ColliderShape.Intersects(item.PhysicsObject.ColliderShape);

                if (isHit)
                {
                    if (onHit != null)
                        onHit(item.PhysicsObject);

                    result = true;
                }
            }

            return result;
        }

        void Awake()
        {
            mTriggerInfoList = new List<TriggerInfo>(30);
            mColliderObjectList = new List<ColliderObject>(30);
        }

        void OnDestroy()
        {
            applicationIsQuitting = true;
        }

        public void Update()
        {
            mIsUpdateing = true;

            var max = mTriggerInfoList.Count;
            for (int x = 0; x < max; x++)
            {
                var triggerInfoX = mTriggerInfoList[x];

                if (triggerInfoX.RemoveMark) continue;

                for (int y = 0; y < max; y++)
                {
                    var triggerInfoY = mTriggerInfoList[y];

                    if (triggerInfoX.RemoveMark) continue;
                    if (triggerInfoY.RemoveMark) continue;
                    if (triggerInfoX.TriggerObject == triggerInfoY.TriggerObject) continue;

                    var isHit = triggerInfoX.PhysicsObject.ColliderShape.Intersects(triggerInfoY.PhysicsObject.ColliderShape);

                    if (!isHit)
                    {
                        if (triggerInfoX.TriggerEnterList.Contains(triggerInfoY))
                        {
                            triggerInfoX.TriggerEnterList.Remove(triggerInfoY);

                            triggerInfoX.TriggerObject.ExecuteOnTriggerExit(triggerInfoY.PhysicsObject);
                        }
                    }
                }//第一遍循环，优先检测Exit.

                for (int y = 0; y < max; y++)
                {
                    var triggerInfoY = mTriggerInfoList[y];

                    if (triggerInfoX.RemoveMark) continue;
                    if (triggerInfoY.RemoveMark) continue;
                    if (triggerInfoX.TriggerObject == triggerInfoY.TriggerObject) continue;

                    var isHit = triggerInfoX.PhysicsObject.ColliderShape.Intersects(triggerInfoY.PhysicsObject.ColliderShape);

                    if (isHit)
                    {
                        if (!triggerInfoX.TriggerEnterList.Contains(triggerInfoY))
                        {
                            triggerInfoX.TriggerEnterList.Add(triggerInfoY);

                            triggerInfoX.TriggerObject.ExecuteOnTriggerEnter(triggerInfoY.PhysicsObject);
                        }
                        else
                        {
                            triggerInfoX.TriggerObject.ExecuteOnTriggerStay(triggerInfoY.PhysicsObject);
                        }
                    }
                }//第二遍循环，检测Enter和Stay.
            }

            mIsUpdateing = false;

            //处理移除
            for (int i = mTriggerInfoList.Count - 1; i >= 0; i--)
            {
                if (mTriggerInfoList[i].RemoveMark)
                    mTriggerInfoList.RemoveAt(i);
            }
        }
    }
}
